﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Data;
using System.Data.SqlClient;
using System.Runtime.CompilerServices;
using System.Security.Permissions;
using System.Web;
using System.Web.Caching;

[SqlClientPermission(SecurityAction.Demand, Unrestricted = true)]
public class SqlSiteMapProvider : StaticSiteMapProvider
{
    const string sql = "select [m_id], [m_name], [m_url], [m_parentid] from lt_Menu order by [m_parentid] asc";
    const string m_CacheKey = "SQLSiteMapProvider_Nodes";
    const string CacheDependencyName = "__SiteMapCacheDependency";

    private int m_IndexID, m_IndexTitle, m_IndexUrl, m_IndexDesc, m_IndexRoles, m_IndexParent;

    private Dictionary<int, SiteMapNode> m_NodesDictionary = new Dictionary<int, SiteMapNode>(16);
    private readonly object m_Lock = new object();
    private SiteMapNode m_RootNode;

    public override void Initialize(string name, NameValueCollection config)
    {
        // Verify that config isn't null
        if (config == null)
            throw new ArgumentNullException("config");
        // Assign the provider a default name if it doesn't have one
        if (String.IsNullOrEmpty(name))
            name = "SqlSiteMapProvider";
        base.Initialize(name, config);
    }

    [MethodImpl(MethodImplOptions.Synchronized)]
    public override SiteMapNode BuildSiteMap()
    {
        lock (m_Lock)
        {
            object root = HttpRuntime.Cache[m_CacheKey];
            if (root != null)
                return (SiteMapNode)root;

            ClearSiteMap();
            using (IDataReader reader = SqlHelper.Instance.ExecuteReader(sql))
            {
                m_IndexID = reader.GetOrdinal("m_id");
                m_IndexUrl = reader.GetOrdinal("m_url");
                m_IndexTitle = reader.GetOrdinal("m_name");
                m_IndexParent = reader.GetOrdinal("m_parentid");
                // Create the root SiteMapNode and add it to the site map
                m_RootNode = CreateRootNode();
                AddNode(m_RootNode, null);

                // Build a tree of SiteMapNodes underneath the root node
                while (reader.Read())
                {
                    // Create another site map node and add it to the site map
                    SiteMapNode node = CreateSiteMapNodeFromDataReader(reader);
                    AddNode(node, GetParentNodeFromDataReader(reader));
                }
                //Create the array of cache key item names
                string[] keys = new String[1];
                keys[0] = CacheDependencyName;
                //Create a dependency object referencing the array of cachekeys (keys)
                CacheDependency dependency = new CacheDependency(null, keys);
                HttpRuntime.Cache.Insert(m_CacheKey, m_RootNode, dependency,
                                         Cache.NoAbsoluteExpiration,
                                         Cache.NoSlidingExpiration,
                                         CacheItemPriority.NotRemovable,
                                         new CacheItemRemovedCallback(OnSiteMapChanged));
            }
            // Return the root SiteMapNode
            return m_RootNode;
        }
    }

    private void ClearSiteMap()
    {
        Clear();
        m_NodesDictionary.Clear();
        m_RootNode = null;
    }

    private SiteMapNode CreateRootNode()
    {
        SiteMapNode node = new SiteMapNode(this, "0", string.Empty, "首页", "首页");
        m_NodesDictionary.Add(0, node);
        return node;
    }

    protected override SiteMapNode GetRootNodeCore()
    {
        lock (m_Lock)
        {
            BuildSiteMap();
            return m_RootNode;
        }
    }

    // Helper methods
    private SiteMapNode CreateSiteMapNodeFromDataReader(IDataReader reader)
    {
        int id = reader.GetInt32(m_IndexID);
        // Get title, URL, description, and roles from the DataReader
        string title = reader.IsDBNull(m_IndexTitle) ? null : reader.GetString(m_IndexTitle).Trim();
        string url = reader.IsDBNull(m_IndexUrl) ? null : reader.GetString(m_IndexUrl).Trim();
        string description = title;
        // Create a SiteMapNode
        SiteMapNode node = new SiteMapNode(this, id.ToString(), url, title, description);
        // Record the node in the m_NodesDictionary dictionary
        m_NodesDictionary.Add(id, node);
        // Return the node        
        return node;
    }

    private SiteMapNode GetParentNodeFromDataReader(IDataReader reader)
    {
        // Get the parent ID from the DataReader
        int pid = reader.GetInt32(m_IndexParent);
        // Return the parent SiteMapNode
        if (!m_NodesDictionary.ContainsKey(pid))
            return null;
        return m_NodesDictionary[pid];
    }

    public void OnSiteMapChanged(string key, object item, CacheItemRemovedReason reason)
    {
        lock (m_Lock)
        {
            if (key == CacheDependencyName && reason == CacheItemRemovedReason.DependencyChanged)
            {
                // Clear the site map
                ClearSiteMap();
            }
        }
    }
}